#pragma once
#include <sstream>
#include <ctime>
#include "Config.hpp"
#include "httplib.h"
#include "Data.hpp"
#include "Util.hpp"

extern cloud::DataManager *_data;
namespace cloud
{
    class Service
    {
    private:
        int _server_port;
        std::string _server_ip;
        std::string _download_prefix;
        httplib::Server _server;

    private:
        static void Upload(const httplib::Request &req, httplib::Response &rsp)
        {
            auto ret = req.has_file("file");
            if (ret == false)
            {
                rsp.status = 400;
                return;
            }
            const auto &file = req.get_file_value("file");
            std::string back_dir = Config::GetInstance()->GetBackDir();
            std::string real_path = back_dir + FileUtil(file.filename).FileName();
            FileUtil fu(real_path);
            fu.SetContent(file.content);
            BackupInfo bf;
            bf.NewBackupInfo(real_path);
            _data->Insert(bf);
        }
        static std::string TimetoStr(time_t time)
        {
            std::string ret = std::ctime(&time);
            return ret;
        }
        static void ListShow(const httplib::Request &req, httplib::Response &rsp)
        {
            std::vector<BackupInfo> arry;
            _data->GetAll(&arry);
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for (auto &a : arry)
            {
                ss << "<tr>";
                ss << "<td><a href='" << a.url_path << "'>" << FileUtil(a.real_path).FileName() << "</a></td>";
                ss << "<td align='right'>" << TimetoStr(a.mtime) << "</td>";
                ss << "<td align='right'>" << a.fsize / 1024 << "k</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "");
            rsp.status = 200;
        }
        static std::string GetEtag(BackupInfo &info)
        {
            std::string ret;
            FileUtil fu(info.real_path);
            ret += fu.FileName();
            ret += "-";
            ret += std::to_string(info.fsize);
            ret += "-";
            ret += std::to_string(info.mtime);
            return ret;
        }
        static void Download(const httplib::Request &req, httplib::Response &rsp)
        {
            BackupInfo bf;
            _data->GetOneByUrl(req.path, &bf);
            if (bf.pack_flag == true)
            {
                FileUtil fu(bf.pack_path);
                fu.UnCompress(bf.real_path);
                fu.Remove();
                bf.pack_flag = false;
                _data->Update(bf);
            }
            FileUtil tmp(bf.real_path);
            bool flag = false;
            if (req.has_header("If-Range"))
            {
                std::string old_etag = req.get_header_value("If-Range");
                if (old_etag == GetEtag(bf))
                {
                    flag = true;
                }
            }
            if (flag == false)
            {
                tmp.GetContent(&rsp.body);
                rsp.set_header("Content-Type", "application/octet-stream");
                rsp.set_header("ETag", GetEtag(bf));
                rsp.set_header("Accept-Ranges", "bytes");
                rsp.status = 200;
            }
            else
            {
                tmp.GetContent(&rsp.body);
                rsp.set_header("Content-Type", "application/octet-stream");
                rsp.set_header("ETag", GetEtag(bf));
                rsp.set_header("Accept-Ranges", "bytes");
                rsp.status = 206;
            }
        }

    public:
        Service()
        {
            Config *config = Config::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
        }
        bool RunModule()
        {
            _server.Post("/upload", Upload);
            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow);
            std::string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, Download);
            _server.listen(_server_ip.c_str(), _server_port);
            return true;
        }
    };
}
